# -------------------------------------01 加载路透社数据集-----------------------
from keras.datasets import reuters

(train_data, train_labels), (test_data, test_labels) = reuters.load_data(
    num_words=10000)  # 与 IMDB 数据集一样，参数 num_words=10000 将数据限定为前 10 000 个最常出现的单词。
# print(len(train_data))  # 8982
# print(len(test_data))  # 2246
# print(train_data[0])  # 与 IMDB 评论一样，每个样本都是一个整数列表（表示单词索引）。#[1, 2, 2, 8, 43, 10, 447, 5, 25, 207, 270, 5, 3095, 111, 16, 369, 186, 90, 67, 7, 89, 5, 19, 102, 6, 19, 124, 15, 90, 67, 84, 22, 482, 26, 7, 48, 4, 49, 8, 864, 39, 209, 154, 6, 151, 6, 83, 11, 15, 22, 155, 11, 15, 7, 48, 9, 4579, 1005, 504, 6, 258, 6, 272, 11, 15, 22, 134, 44, 11, 15, 16, 8, 197, 1245, 90, 67, 52, 29, 209, 30, 32, 132, 6, 109, 15, 17, 12]
# 如果好奇的话，你可以用下列代码将索引解码为单词。
# word_index = reuters.get_word_index()
# reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])
# decoded_newswire = ' '.join([reverse_word_index.get(i - 3, '?') for i in
#                              train_data[0]])
# print(decoded_newswire)  # ? ? ? said as a result of its december acquisition of space co it expects earnings per share in 1987 of 1 15 to 1 30 dlrs per share up from 70 cts in 1986 the company said pretax net should rise to nine to 10 mln dlrs from six mln dlrs in 1986 and rental operation revenues to 19 to 22 mln dlrs from 12 5 mln dlrs it said cash flow per share this year should be 2 50 to three dlrs reuter 3
# 样本对应的标签是一个 0~45 范围内的整数，即话题索引编号。
# print(train_labels[0])  # 3


# -------------------------------------02 准备数据-----------------------
# 将数据向量化
import numpy as np


def vectorize_sequences(sequences, dimension=10000):
    results = np.zeros((len(sequences), dimension))
    for i, sequence in enumerate(sequences):
        results[i, sequence] = 1.
    return results


x_train = vectorize_sequences(train_data)  # 将训练数据向量化
x_test = vectorize_sequences(test_data)  # 将测试数据向量化


# 将标签向量化有两种方法：你可以将标签列表转换为整数张量，或者使用 one-hot 编码。
# one-hot 编码是分类数据广泛使用的一种格式，也叫分类编码（categorical encoding）。 6.1 节给出
# 了 one-hot 编码的详细解释。在这个例子中，标签的 one-hot 编码就是将每个标签表示为全零向量，
# 只有标签索引对应的元素为 1。其代码实现如下。
def to_one_hot(labels, dimension=46):
    results = np.zeros((len(labels), dimension))
    for i, label in enumerate(labels):
        results[i, label] = 1.
    return results


one_hot_train_labels = to_one_hot(train_labels)  # 将训练标签向量化
one_hot_test_labels = to_one_hot(test_labels)  # 将测试标签向量化
# 注意， Keras 内置方法可以实现这个操作，你在 MNIST 例子中已经见过这种方法。
# from keras.utils.np_utils import to_categorical
#
# one_hot_train_labels = to_categorical(train_labels)
# one_hot_test_labels = to_categorical(test_labels)

# -------------------------------------03 构建网络-----------------------
# 这个主题分类问题与前面的电影评论分类问题类似，两个例子都是试图对简短的文本片段
# 进行分类。但这个问题有一个新的约束条件：输出类别的数量从 2 个变为 46 个。输出空间的维
# 度要大得多。
# 对于前面用过的 Dense 层的堆叠，每层只能访问上一层输出的信息。如果某一层丢失了与
# 分类问题相关的一些信息，那么这些信息无法被后面的层找回，也就是说，每一层都可能成为
# 信息瓶颈。上一个例子使用了 16 维的中间层，但对这个例子来说 16 维空间可能太小了，无法
# 学会区分 46 个不同的类别。这种维度较小的层可能成为信息瓶颈，永久地丢失相关信息。
# 出于这个原因，下面将使用维度更大的层，包含 64 个单元。

# 模型定义
from keras import models
from keras import layers

model = models.Sequential()
model.add(layers.Dense(128, activation='relu', input_shape=(10000,)))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dense(46, activation='softmax'))
# 关于这个架构还应该注意另外两点。
#  网络的最后一层是大小为 46 的 Dense 层。这意味着，对于每个输入样本，网络都会输
# 出一个 46 维向量。这个向量的每个元素（即每个维度）代表不同的输出类别。
#  最后一层使用了 softmax 激活。你在 MNIST 例子中见过这种用法。网络将输出在 46
# 个不同输出类别上的概率分布——对于每一个输入样本，网络都会输出一个 46 维向量，
# 其中 output[i] 是样本属于第 i 个类别的概率。 46 个概率的总和为 1
# 对于这个例子，最好的损失函数是 categorical_crossentropy（分类交叉熵）。它用于
# 衡量两个概率分布之间的距离，这里两个概率分布分别是网络输出的概率分布和标签的真实分
# 布。通过将这两个分布的距离最小化，训练网络可使输出结果尽可能接近真实标签。


# -------------------------------------04 编译模型-----------------------

model.compile(optimizer='rmsprop',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

# -------------------------------------05 验证你的方法-----------------------
# 我们在训练数据中留出 1000 个样本作为验证集。
x_val = x_train[:1000]
partial_x_train = x_train[1000:]
y_val = one_hot_train_labels[:1000]
partial_y_train = one_hot_train_labels[1000:]

# -------------------------------------06 训练模型-----------------------
history = model.fit(partial_x_train,
                    partial_y_train,
                    epochs=9,
                    batch_size=512,
                    validation_data=(x_val, y_val))
# -------------------------------------07 绘制训练损失和验证损失-----------------------
import matplotlib.pyplot as plt

loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(1, len(loss) + 1)
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.xlabel('Epochs')
plt.ylabel('Loss')
plt.legend()
plt.show(block=False)
plt.show()
# -------------------------------------08 绘制训练精度和验证精度-----------------------
plt.clf()
acc = history.history['acc']
val_acc = history.history['val_acc']
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show(block=False)
plt.show()
results = model.evaluate(x_test, one_hot_test_labels)
print(results) #[1.5904786782082128, 0.73018699913606622]
# print(model.metrics_names)